package com.chuangjiangx.member.basic.ddd.application;

import com.alibaba.fastjson.JSON;
import com.chuangjiangx.commons.exception.BaseException;
import com.chuangjiangx.dddbase.application.Application;
import com.chuangjiangx.member.basic.ddd.application.command.AIFaceDalCommand;
import com.chuangjiangx.member.basic.ddd.application.command.ModifyMemberCommand;
import com.chuangjiangx.member.basic.ddd.application.command.ModifyMobileCommand;
import com.chuangjiangx.member.basic.ddd.application.command.RegisterMemberCommand;
import com.chuangjiangx.member.basic.ddd.domain.model.*;
import com.chuangjiangx.member.coupon.ddd.application.command.CancelCardCommand;
import com.chuangjiangx.member.coupon.ddd.application.command.CancelMemberCommand;
import com.chuangjiangx.member.coupon.ddd.domain.model.MbrWxCard;
import com.chuangjiangx.member.coupon.ddd.domain.repository.MbrWxCardRepository;
import com.chuangjiangx.member.coupon.ddd.domain.model.MbrCoupon;
import com.chuangjiangx.member.coupon.ddd.domain.repository.MbrCouponRepository;
import com.chuangjiangx.member.coupon.ddd.domain.repository.MbrHasCouponRepository;
import com.chuangjiangx.member.basic.ddd.domain.service.MemberMsgDomainService;
import com.chuangjiangx.member.basic.ddd.domain.service.MemberDomainService;
import com.chuangjiangx.member.basic.ddd.domain.service.MemberRedisDomainService;
import com.chuangjiangx.member.basic.ddd.domain.exception.MobileExistException;
import com.chuangjiangx.member.basic.ddd.domain.service.model.CreateMember;
import com.chuangjiangx.domain.shared.model.Sex;
import com.chuangjiangx.member.score.ddd.application.command.ExchangeGoodsCommand;
import com.chuangjiangx.member.score.ddd.domain.model.*;
import com.chuangjiangx.member.score.ddd.domain.repository.MbrScoreGiftRecordRepository;
import com.chuangjiangx.member.score.ddd.domain.repository.MbrScoreGiftRuleRepository;
import com.chuangjiangx.member.score.ddd.domain.repository.MbrScoreStreamRepository;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderRefundRepository;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderRepository;
import com.chuangjiangx.member.stored.ddd.domain.MbrRandomUtils;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrStoredStreamRepository;
import com.chuangjiangx.member.coupon.ddd.query.MbrCouponQuery;
import com.chuangjiangx.member.basic.ddd.query.MemberQuery;
import com.chuangjiangx.member.coupon.ddd.dal.dto.MbrHasCouponList;
import com.chuangjiangx.member.basic.ddd.query.dto.MemberDTO;
import com.chuangjiangx.member.coupon.ddd.domain.model.MbrCouponId;
import com.chuangjiangx.member.basic.ddd.domain.model.MerchantId;
import com.chuangjiangx.member.basic.ddd.domain.model.MerchantUserId;
import com.chuangjiangx.member.basic.ddd.domain.model.StoreId;
import com.chuangjiangx.member.basic.ddd.domain.model.StoreUserId;
import com.chuangjiangx.member.common.utils.AIFace.*;
import com.chuangjiangx.member.common.utils.RequestUtils;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import weixin.popular.api.CardAPI;
import weixin.popular.bean.BaseResult;

import java.math.BigDecimal;
import java.util.*;

/**
 * 会员操作类
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class MemberApplication implements Application {

    @Autowired
    private MemberDomainService memberDomainService;

    @Autowired
    private MemberRepository memberRepository;

    @Autowired
    private MbrAccountRepository mbrAccountRepository;

    @Autowired
    private MbrUserMappingRepository mbrUserMappingRepository;
    @Autowired
    private MemberMsgDomainService memberMsgDomainService;

    @Autowired
    private MbrScoreGiftRecordRepository mbrScoreGiftRecordRepository;

    @Autowired
    private MbrStoredStreamRepository mbrStoredStreamRepository;

    @Autowired
    private MbrScoreStreamRepository mbrScoreStreamRepository;
    @Autowired
    private MemberRedisDomainService memberRedisDomainService;
    @Autowired
    private MbrScoreGiftRuleRepository mbrScoreGiftRuleRepository;
    @Autowired
    private MbrOrderRepository mbrOrderRepository;
    @Autowired
    private MbrOrderRefundRepository mbrOrderRefundRepository;
    @Autowired
    private MbrHasCouponRepository mbrHasCouponRepository;
    @Autowired
    private MemberQuery memberQuery;

    @Autowired
    private MbrWxCardRepository mbrWxCardRepository;
    @Autowired
    private MbrCouponQuery mbrCouponQuery;
    @Autowired
    private MbrCouponRepository mbrCouponRepository;

    /**
     * 微信授权后跳转到登录页点击登录后的自动注册会员操作
     * 必须要有手机号码和openid
     * @param command 注册信息
     */
     Member wxAutoRegisterMember(RegisterMemberCommand command, MbrUserMappingType mbrUserMappingType)  {
        Member member  = clientRegisterMember(command);

        //绑定
         memberDomainService.bindWX(member,new MbrUserMapping(
                 member.getId(),
                 new MerchantId(command.getMerchantId()),
                 command.getOpenid(),
                 command.getMopenid(),
                 mbrUserMappingType,
                 command.getAopenId(),
                 null
         ));
        return member;
    }

    /**
     * 客户端注册会员
     * @param command
     * @return
     */
    public Member clientRegisterMember(RegisterMemberCommand command){
        //根据手机号码查询
        Member member = memberRepository.fromMerchantIdAndMobile(new MerchantId(command.getMerchantId()), command.getMobile());
        if(member == null){
            //注册会员
            Optional<RegisterMemberCommand> optional = Optional.of(command);
            OperationInfo operationInfo = new OperationInfo(
                    new MerchantId(command.getMerchantId()),
                    optional.map(o -> o.getStoreId()).map(o -> new StoreId(o)).orElse(null),
                    optional.map(o -> o.getStoreUserId()).map(o -> new StoreUserId(o)).orElse(null),
                    optional.map(o -> o.getTerminalType()).map(o -> MbrSourceTerminal.getSourceTerminal(o)).orElse(null),
                    optional.map(o -> o.getTerminalNum()).map(o -> o).orElse(null),
                    optional.map(o -> o.getMerchantUserId()).map(o -> new MerchantUserId(o)).orElse(null)
            );
            CreateMember createMember = new CreateMember(
                    command.getName(),
                    null == command.getSex() ? Sex.MALE : Sex.getSex(command.getSex()),
                    command.getMobile(),
                    command.getHeadimgurl(),
                    command.getBirthday(),
                    operationInfo
            );
            member = memberDomainService.registerMember(createMember);
        }
        return member;
    }


    /**
     * 会员信息修改
     *
     * @param command 会员修改信息
     */
    public void modifyMember(ModifyMemberCommand command) {

        Assert.notNull(command.getMemberId(), " 会员信息修改 会员id不能为空");

        Member member = memberRepository.fromId(new MemberId(command.getMemberId()));
        Assert.notNull(member, "会员不存在");

        OperationInfo operationInfo = new OperationInfo(
                new MerchantId(command.getMerchantId()== null ? null : command.getMerchantId()),
                command.getStoreId() == null ? null : new StoreId(command.getStoreId()),
                command.getStoreUserId() == null ? null : new StoreUserId(command.getStoreUserId()),
                member.getOperationInfo() == null ? null : member.getOperationInfo().getTerminalType(),
                command.getTerminalNum() == null ? null : command.getTerminalNum(),
                new MerchantUserId(command.getMerchantUserId()== null ? null : command.getMerchantUserId())
        );

        //修改会员信息
        member.modifyBasicInfo(command.getName(), Sex.getSex(command.getSex()), command.getBirthday(), command.getHeadimgurl(), operationInfo);
        memberRepository.update(member);
    }

    /**
     * 会员信息修改（APP）
     * @param command 会员修改信息
     */
    public void modifyMemberApp(ModifyMemberCommand command) throws Exception {

        Assert.notNull(command.getMemberId(), " 会员信息修改 会员id不能为空");

        Member member = memberRepository.fromId(new MemberId(command.getMemberId()));
        Assert.notNull(member, "会员不存在");
        //修改会员信息
        member.modifyBasicInfo(command.getName(), Sex.getSex(command.getSex()), command.getBirthday(), command.getHeadimgurl(),null);
        memberRepository.update(member);
    }


    /**
     * 修改手机号
     */
    public void modifyMobile(ModifyMobileCommand command) throws Exception {
        Assert.notNull(command.getMemberId(), "修改手机号 会员id不能为空");

        Member member = memberRepository.fromId(new MemberId(command.getMemberId()));
        Assert.notNull(member, "会员不存在");

        OperationInfo operationInfo = new OperationInfo(new MerchantId(command.getMerchantId()),
                command.getStoreId() == null ? null : new StoreId(command.getStoreId()),
                command.getStoreUserId() == null ? null : new StoreUserId(command.getStoreUserId()),
                command.getTerminalType() == null ? null : MbrSourceTerminal.getSourceTerminal(command.getTerminalType()),
                command.getTerminalNum() == null ? null : command.getTerminalNum(),
                new MerchantUserId(command.getMerchantUserId()));

        // 修改手机号处理逻辑
        if (!command.getMobile().equals(member.getMobile())) {
            //验证手机号是否重复
            long num = memberRepository.countByMerchantIdAndMobile(operationInfo.getMerchantId(),
                    command.getMobile());
            if (num != 0) {
                throw new MobileExistException();
            }
        } else {
            throw new Exception("当前输入手机号码与原手机号码重复！");
        }
        //修改手机号
        member.modifyMobile(command.getMobile(), operationInfo);
        memberRepository.update(member);
    }


    /**
     * 会员注销
     */
    public void cancelMember(CancelMemberCommand command) throws Exception {

        Assert.notNull(command.getMemberId(), "会员id不能为空");

        Member member = memberRepository.fromId(new MemberId(command.getMemberId()));
        Assert.notNull(member, "会员不存在");

        OperationInfo operationInfo = new OperationInfo(new MerchantId(command.getMerchantId()),
                command.getStoreId() == null ? null : new StoreId(command.getStoreId()),
                command.getStoreUserId() == null ? null : new StoreUserId(command.getStoreUserId()),
                command.getTerminalType() == null ? null : MbrSourceTerminal.getSourceTerminal(command.getTerminalType()),
                command.getTerminalNum() == null ? null : command.getTerminalNum(),
                new MerchantUserId(command.getMerchantUserId()));

        Assert.isTrue(operationInfo.getMerchantId().getId()
                == member.getOperationInfo().getMerchantId().getId(), "该会员不属于当前商户");

        MbrAccount mbrAccount = mbrAccountRepository.findMbrAccountByMemberId(new MemberId(member.getId().getId()));

        if (mbrAccount.getAvailableBalance().compareTo(BigDecimal.ZERO) == 0) {
            //判断是否有余额
            MbrWxCard wxCard = mbrWxCardRepository.fromMerchantId(new MerchantId(command.getMerchantId()));
            String accessToken = memberRedisDomainService.getMerchantAccessToken(command.getMerchantId());
            //用户未使用的卡券设置为无效状态
            List<MbrHasCouponList> mbrHasCoupons = mbrCouponQuery.findMbrHasCouponList(member.getId().getId(), 0);
            mbrHasCoupons.forEach(mbrHasCoupon -> {
                MbrCoupon mbrCoupon = mbrCouponRepository.fromId(new MbrCouponId(mbrHasCoupon.getMbrCouponId()));
                CancelCardCommand memberCommand = new CancelCardCommand();
                memberCommand.setCard_id(mbrCoupon.getWxCardId());
                memberCommand.setCode(mbrHasCoupon.getVerifyCode());
                String requestJson = JSON.toJSONString(memberCommand);
                BaseResult result = CardAPI.codeUnavailable(accessToken, requestJson);
                log.info(JSON.toJSONString(result));
            });
            //用户的会员卡设置成无效状态
            if (wxCard != null) {
                CancelCardCommand memberCommand = new CancelCardCommand();
                memberCommand.setCard_id(wxCard.getWxCardId());
                memberCommand.setCode(member.getMemberCardNum());
                String requestJson = JSON.toJSONString(memberCommand);
                BaseResult result = CardAPI.codeUnavailable(accessToken, requestJson);
                log.info(JSON.toJSONString(result));
            }
            if(StringUtils.isNotBlank(member.getFaceId())){
                //请求人脸库删除人脸信息
                AIDeleteFaceInfoRequest r = new AIDeleteFaceInfoRequest();
                r.setUnion_id(System.getProperty("CUSTOMER_PRODUCT_ID") + "-" + member.getOperationInfo().getMerchantId().getId());
                r.setFace_ids(Arrays.asList(member.getFaceId()));
                JavaType javaType = TypeFactory.defaultInstance().constructParametricType(AIBaseResponse.class, AIDeleteFaceInfoResult.class);
                RequestUtils.ReqResponse<Object> reqResponse = RequestUtils.postForObject(AIConstant.AiFaceUrl.DELETE_FACE, r, javaType);
                if(reqResponse.isSuccess()){
                    log.info("删除人脸信息:{}",JSON.toJSONString(reqResponse.getData()));
                }else{
                    log.info("删除人脸信息失败:{}",JSON.toJSONString(reqResponse.getStatus().msg));
                }
            }

            //删除会员信息
            memberRepository.deleteById(member.getId().getId());

            //删除账户信息
            mbrAccountRepository.deleteByMemberId(member.getId().getId());

            //删除映射关系表中数据
            mbrUserMappingRepository.deleteByMemberId(new MemberId(member.getId().getId()), new MerchantId(command.getMerchantId()));

            //删除积分兑换记录
            mbrScoreGiftRecordRepository.deleteByMemberId(member.getId().getId());

            //删除积分流水
            mbrStoredStreamRepository.deleteByMemberId(member.getId().getId());

            // 删除储值流水
            mbrScoreStreamRepository.deleteByMemberId(member.getId().getId());
            // 删除卡券领取记录
        } else {
            throw new Exception("有余额不能删除！");
        }
    }


    /**
     * 会员兑换商品
     * 客户端直接使用积分兑换商品(不是使用提货码的方式)
     */
    public MbrScoreGiftRecord exchangeGoods(ExchangeGoodsCommand exchangeGoodsCommand) {
        Member member = memberRepository.fromId(new MemberId(exchangeGoodsCommand.getMemberId()));
        Assert.notNull(member, "会员信息不存在");

        MbrScoreGiftRule mbrScoreGiftRule = mbrScoreGiftRuleRepository.fromId(new MbrScoreGiftRuleId(exchangeGoodsCommand.getScoreGiftRuleId()));

        Long merchantId = member.getOperationInfo().getMerchantId().getId();
        Assert.isTrue(Objects.equals(merchantId, exchangeGoodsCommand.getMerchantId()), "该会员不属于当前商户");

        MbrAccount mbrAccount = mbrAccountRepository.findMbrAccountByMemberId(member.getId());
        mbrScoreGiftRule.check();
        if(mbrScoreGiftRule.remainGiftCount() <=0){
            throw new BaseException("0000","剩余可兑换次数不足");
        }
        //每人限制兑换验证
        long giftCount = mbrScoreGiftRecordRepository.queryGiftCount(member.getId(), mbrScoreGiftRule.getId());
        if(mbrScoreGiftRule.getExchangeLimit().longValue() <= giftCount){
            throw new BaseException("0000","超出每人兑换限制");
        }
        //用于比较商品兑换积分与用户可用积分大小，结果是:-1 小于,0 等于,1 大于
        if(mbrAccount.getAvailableScore().compareTo(mbrScoreGiftRule.getScore()) < 0 ){
            throw new BaseException("0000","会员积分不足");
        }

        //减分操作
        mbrAccount.changeScore(mbrScoreGiftRule.getScore(), MbrScoreType.EXCHANGE);

        //保存积分明细
        MbrScoreStream mbrScoreStream = new MbrScoreStream(
                member.getId(),
                mbrScoreGiftRule.getScore(),
                MbrScoreType.EXCHANGE,
                null,
                mbrScoreGiftRule.getId(),
                null,
                null,
                mbrAccount.getAvailableScore(),
                mbrScoreGiftRule.getName(),
                null,
                exchangeGoodsCommand.getMerchantUserId(),
                exchangeGoodsCommand.getStoreUserId(),
                exchangeGoodsCommand.getStoreId()
        );
        mbrScoreStreamRepository.save(mbrScoreStream);

        //保存兑换信息
        MbrScoreGiftRecord mbrScoreGiftRecord = new MbrScoreGiftRecord(
                null,
                member.getId(),
                mbrScoreGiftRule.getId(),
                new MerchantId(merchantId),
                exchangeGoodsCommand.getStoreId(),
                exchangeGoodsCommand.getStoreUserId(),
                exchangeGoodsCommand.getMerchantUserId(),
                new Date(),null,
                Objects.equals(MbrScoreExchangeType.OFFLINE, exchangeGoodsCommand.getScoreExchangeType()) ? null : MbrRandomUtils.generateMbrClaimCode(),
                Objects.equals(MbrScoreExchangeType.OFFLINE, exchangeGoodsCommand.getScoreExchangeType()) ? ClaimStatus.CLAIMED : ClaimStatus.UNCLAIM,
                Objects.equals(MbrScoreExchangeType.OFFLINE, exchangeGoodsCommand.getScoreExchangeType()) ? new Date() : null,
                mbrScoreStream.getId()
        );
        mbrScoreGiftRecordRepository.save(mbrScoreGiftRecord);

        //积分兑换规则计数累加
        mbrScoreGiftRuleRepository.accumulateCount(mbrScoreGiftRule.getId().getId());

        mbrAccountRepository.update(mbrAccount);
        //推送积分变动信息
        memberMsgDomainService.sendMbrScoreChangeMsg(merchantId,mbrAccount,mbrScoreStream,"积分兑换");

        return mbrScoreGiftRecord;
    }





    /**
     * 获取商户微信公众号二维码
     * @param merchantId 商户id
     * @return
     */
    public String getMerchantQrcodeUrl(Long merchantId) {
        return memberRedisDomainService.getMerchantWxQrcodeUrl(merchantId);
    }



    /**
     * 更新FaceId
     * @param command
     */
    public void updateAIFaceMember(AIFaceDalCommand command) throws AiException {
        MemberDTO member = memberQuery.queryByMemberId(command.getMbrUserContext().getMemberId());
        Member _member = memberRepository.fromId(new MemberId(member.getId()));
        switch (command.getAiMethod()) {
            case ADD_FACE:
                AiAddFaceResponse response = (AiAddFaceResponse) command.getAiResponse();
                _member.modifyFaceId(response.getFaceId().toString());
                memberRepository.update(_member);
                break;
            case UPDATE_FACE:
                AIUpdateFaceResponse updateResponse = (AIUpdateFaceResponse) command.getAiResponse();
                _member.modifyFaceId((String) updateResponse.getFaceId());
                memberRepository.update(_member);
                break;
            default:
                throw new AiException("method的为空");
        }
    }
}
